package com.xing.asm.simple;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;

import java.io.FileOutputStream;
import java.io.IOException;

public class GetParameters extends ClassLoader implements Opcodes {
	
	private static class Foo {
		
		public static Product execute(User user) {

			System.out.println("-----------" + user.getName());

			return user.getProduct();
		}
	}
	
	
	public static void main(String[] args) throws IOException, IllegalArgumentException, SecurityException {
		
		ClassReader cr = new ClassReader(Foo.class.getName());
		ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
		ClassVisitor cv = new MethodChangeClassAdapter(cw);
		cr.accept(cv, Opcodes.ASM5);
		
		byte[] code = cw.toByteArray();
		
		FileOutputStream fos = new FileOutputStream("E:\\ljx\\Example1.class");
		fos.write(code);
		fos.close();
		
		User user = new User(1, 1, "IamaUser", new Product("Iphone", 5999.08));
		
		Foo foo = new Foo();
		foo.execute(user);
	}
	
	static class MethodChangeClassAdapter extends ClassVisitor implements Opcodes {
		
		public MethodChangeClassAdapter(final ClassVisitor cv) {
			super(Opcodes.ASM4, cv);
		}
		
		@Override
		public MethodVisitor visitMethod(int access, String name, final String desc, String signature, String[] exceptions) {
			
			MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
			try {
//				final ParameterAttribute param = new ParameterAttribute(0, "aaa", "com.xx.User2", "getProduct().getName()", null);
				
				if (!"execute".equals(name)) {
					return mv;
				}
				
				final Type type = Type.getType(User.class);
				
				
				mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {
					@Override
					protected void onMethodEnter() {
						
						
						Label startCatch = newLabel();
						Label endCatch = newLabel();
						
						visitLabel(startCatch);
						
//						super.getStatic(ProxyUtils.AGENT_BRIDGE_TYPE, "publicApi", ProxyUtils.PUBLIC_API_TYPE);
//
//						PublicApi api = BytecodeGenProxyBuilder.newBuilder(PublicApi.class, this, false).build();
						
						push("name1");
						
						loadArg(0);
						
						if (type.getSort() != Type.OBJECT) {
							box(type);
						}
						
						Label objectLabel = newLabel();
						Label skipLabel = newLabel();
						Label popStackLabel = newLabel();
						
						dup();
						
						// 判断参数是否为空
						ifNull(popStackLabel);
						
						visitLabel(objectLabel);
						
//						if (param.getInvokerChains() == null) {
//							invokeVirtual(Type.getType(Object.class), new Method("toString", Type.getType(String.class), new Type[0]));
//						} else {
//							String[] invokerChains = param.getInvokerChains();
//
//							Class paramClass = User2.class;
//
//							if (paramClass == null) {
//								invokeVirtual(Type.getType(Object.class), new Method("toString", Type.getType(String.class), new Type[0]));
//							} else {
//								for (String methodName : invokerChains) {
//									java.lang.reflect.Method invokerMethod = Invoker.getAccessibleMethodByName(paramClass, methodName);
//
//									invokeVirtual(Type.getType(paramClass), new Method(methodName, Type.getType(invokerMethod.getReturnType()), new Type[0]));
//
//									paramClass = invokerMethod.getReturnType();
//								}
//
//							}
//
//						}

//						api.addCustomParameter("", "");
						
						goTo(skipLabel);
						
						visitLabel(popStackLabel);
						pop();
						pop();
						pop();
						
						visitLabel(skipLabel);
						
						goTo(endCatch);
						
						Label handler = new Label();
						visitLabel(handler);
						
						final int throwableLocal = newLocal(Type.getType(Throwable.class));
						storeLocal(throwableLocal);
						
						Runnable throwableMessage = new Runnable() {
							public void run() {
								loadLocal(throwableLocal);
							}
						};
						
						
//						ProxyUtils.getLogger(this).debug("{}.{}{} threw an exception: {}", throwableMessage);
						
						visitLabel(endCatch);
						
						visitTryCatchBlock(startCatch, endCatch, handler, Type.getType(Throwable.class).getInternalName());
						
					}
					
					@Override
					protected void onMethodExit(int opcode) {
						// 原方法的返回值位于栈顶
						
						// 获取返回值类型
						Type tp = Type.getReturnType(desc);
						
						// 创建一个返回值类型的变量
						int i = newLocal(tp);
						
						// 将返回值存放在该变量中
						mv.visitVarInsn(Opcodes.ASTORE, i);
						
						// 强制类型转换
						if (tp.getSort() != Type.OBJECT) {
							box(tp);
						}
						
						
						Label startCatch = newLabel();
						Label endCatch = newLabel();
						
						visitLabel(startCatch);

//						getStatic(ProxyUtils.AGENT_BRIDGE_TYPE, "publicApi", ProxyUtils.PUBLIC_API_TYPE);

//						getStatic(ProxyUtils.TRACED_METHOD_TYPE, "CURRENT", ProxyUtils.TRACED_METHOD_TYPE);

//						invokeStatic(ProxyUtils.xx_API_TYPE, new Method("getTracedMethod", ProxyUtils.TRACED_METHOD_TYPE, new Type[0]));

//						TracedMethod api = BytecodeGenProxyBuilder.newBuilder(TracedMethod.class, this, false).build();

//						invokeStatic(ProxyUtils.AGENT_BRIDGE_TYPE, new Method("getAgent", ProxyUtils.AGENT_BRIDGE_AGENT_TYPE, new Type[0]));
//						invokeStatic(ProxyUtils.xx_API_TYPE, new Method("getTracedMethod", Type.getType(TracedMethod.class), new Type[0]));

						
						Label objectLabel = newLabel();
						Label skipLabel = newLabel();
						Label popStackLabel = newLabel();
						
						// 加载返回值
						mv.visitVarInsn(Opcodes.ALOAD, i);
						
						// 判断返回值是否为空
						ifNull(popStackLabel);
						
						visitLabel(objectLabel);
						
						push("aaaa");
						
						mv.visitVarInsn(Opcodes.ALOAD, i);
						visitLabel(objectLabel);
						
//						invokeVirtual(Type.getType(Object.class), new Method("toString", Type.getType(String.class), new Type[0]));

//						invokeStatic(ProxyUtils.xx_API_TYPE, new Method("getTracedMethod", ProxyUtils.TRACED_METHOD_TYPE, new Type[0]));

//						invokeVirtual()

//						BytecodeGenProxyBuilder.newBuilder(TracedMethod.class, this, false).build().addParameters("", "");

//						api.addCustomParameter("","");
						
//						invokeInterface(Type.getType(TracedMethod.class), new Method("addParameters", "(Ljava/lang/String;Ljava/lang/Object;)V"));
						
						goTo(skipLabel);
						
						visitLabel(popStackLabel);
						
						visitLabel(skipLabel);
						
						goTo(endCatch);
						
						Label handler = new Label();
						visitLabel(handler);
						
						final int throwableLocal = newLocal(Type.getType(Throwable.class));
						storeLocal(throwableLocal);
						
						Runnable throwableMessage = new Runnable() {
							public void run() {
								loadLocal(throwableLocal);
							}
						};
						
//						ProxyUtils.getLogger(this).debug("Get return value threw an exception: ", throwableMessage);
						
						visitLabel(endCatch);
						
						visitTryCatchBlock(startCatch, endCatch, handler, Type.getType(Throwable.class).getInternalName());
						
						mv.visitVarInsn(Opcodes.ALOAD, i);
						
					}
				};
			} catch (Exception e) {
			
			}
			return mv;
		}
		
	}
}
